package com.c.opt.query;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.sql.Direction;
import cn.hutool.db.sql.Order;
import cn.hutool.json.JSONUtil;
import com.c.opt.enums.EnumQueryLinkRule;
import com.c.opt.enums.EnumQueryRuleType;
import ntd.util.config.common.exception.ComException;
import ntd.util.frame.action.database.enums.EnumDataLevel;
import ntd.util.frame.data.tool.ToolMysql;

import java.util.ArrayList;
import java.util.List;

/**
 * 查询条件构造器
 */
public class ComOptQueryWrapper {

    // 逻辑删除条件
    private static final ComQueryInfo C_FLAG = new ComQueryInfo("flag", "0", EnumQueryRuleType.RULE_EQ);

    private final String tName;
    // 查询规则
    private List<ComQueryInfo> comQueryInfoList = new ArrayList<>();
    // 排序规则
    private List<Order> orderList = new ArrayList<>();
    // 数据级别
    private EnumDataLevel enumDataLevel = EnumDataLevel.DATA_LEVEL_ONLY_PUBLIC;

    public ComOptQueryWrapper(String tName) {
        this.tName = tName;
    }

    public ComOptQueryWrapper(String tName, List<ComQueryInfo> comQueryInfoList) {
        this.tName = tName;
        this.comQueryInfoList = ListUtil.toList(comQueryInfoList);
    }

    public ComOptQueryWrapper(String tName, List<ComQueryInfo> comQueryInfoList, List<Order> orderList) {
        this.tName = tName;
        this.comQueryInfoList = ListUtil.toList(comQueryInfoList);
        this.orderList = orderList;
    }

    public ComOptQueryWrapper dataLevel(EnumDataLevel enumDataLevel) {
        this.enumDataLevel = enumDataLevel;
        return this;
    }

    public EnumDataLevel dataLevel() {
        return this.enumDataLevel;
    }

    public boolean isEmpty() {
        return CollUtil.isEmpty(comQueryInfoList);
    }

    public ComOptQueryWrapper logicDelFlag() {
        comQueryInfoList.add(0, C_FLAG);
        return this;
    }

    public ComOptQueryWrapper desc(String name) {
        return order(name, Direction.DESC);
    }

    public ComOptQueryWrapper asc(String name) {
        return order(name, Direction.ASC);
    }

    public ComOptQueryWrapper order(String name, Direction asc) {
        return order(new Order(name, asc));
    }

    public ComOptQueryWrapper order(Order order) {
        orderList.add(order);
        return this;
    }

    public ComOptQueryWrapper build(ComQueryInfo comQueryInfo) {
        if (StrUtil.isNotBlank(comQueryInfo.getName()) && (StrUtil.isNotBlank(comQueryInfo.getVal()) || CollUtil.isNotEmpty(comQueryInfo.getValues()))) {
            comQueryInfoList.add(comQueryInfo);
        } else if (comQueryInfo.getQueryRuleType().isNoVal()) {
            comQueryInfoList.add(comQueryInfo);
        }
        return this;
    }

    public ComOptQueryWrapper eq(String key, String val) {
        return build(new ComQueryInfo(key, val, EnumQueryRuleType.RULE_EQ));
    }

    public ComOptQueryWrapper orEq(String key, String val) {
        return build(new ComQueryInfo(EnumQueryLinkRule.RULE_OR, key, val, EnumQueryRuleType.RULE_EQ));
    }

    public ComOptQueryWrapper ne(String key, String val) {
        return build(new ComQueryInfo(key, val, EnumQueryRuleType.RULE_NE));
    }

    public ComOptQueryWrapper like(String key, String val) {
        return build(new ComQueryInfo(key, buildLikeSqlVal(val), EnumQueryRuleType.RULE_LIKE));
    }

    public ComOptQueryWrapper orLike(String key, String val) {
        return build(new ComQueryInfo(EnumQueryLinkRule.RULE_OR, key, buildLikeSqlVal(val), EnumQueryRuleType.RULE_LIKE));
    }

    public ComOptQueryWrapper ge(String key, String val) {
        return build(new ComQueryInfo(key, val, EnumQueryRuleType.RULE_GE));
    }

    public ComOptQueryWrapper le(String key, String val) {
        return build(new ComQueryInfo(key, val, EnumQueryRuleType.RULE_LE));
    }

    public ComOptQueryWrapper in(String key, List<String> values) {
        Opt.ofEmptyAble(values).orElseThrow(() -> new ComException("in 查询条件不能为空！"));
        return build(new ComQueryInfo(key, values, EnumQueryRuleType.RULE_IN));
    }

    public ComOptQueryWrapper isNull(String key) {
        return build(new ComQueryInfo(Opt.ofBlankAble(key).orElseThrow(() -> new ComException("isNull 查询条件不能为空！")), EnumQueryRuleType.RULE_IS_NULL));
    }

    public ComOptQueryWrapper isNotNull(String key) {
        return build(new ComQueryInfo(Opt.ofBlankAble(key).orElseThrow(() -> new ComException("isNull 查询条件不能为空！")), EnumQueryRuleType.RULE_IS_NOT_NULL));
    }

    public String buildSql() {
        StrBuilder strBuilder = StrBuilder.create(StrUtil.format("select t.* from {} as t where 1 = 1", tName));
        comQueryInfoList.forEach(comQueryInfo -> strBuilder.append(buildSqlWhere(comQueryInfo)));
        strBuilder.append(buildSqlOrder());
        return strBuilder.toString();
    }

    public String buildPageSql(int page, int size) {
        return StrUtil.format("{} limit {}, {};", buildSql(), PageUtil.getStart(page - 1, size), size);
    }

    public String buildCountSql() {
        StrBuilder strBuilder = StrBuilder.create(StrUtil.format("select count(1) from {} as t where 1 = 1", tName));
       comQueryInfoList.forEach(comQueryInfo -> strBuilder.append(buildSqlWhere(comQueryInfo)));
        return strBuilder.toString();
    }

    private String buildSqlOrder() {
        if (CollUtil.isEmpty(orderList)) {
            return "";
        }
        StrBuilder strBuilder = StrBuilder.create("");
        for (Order order : orderList) {
            String orderStr = Direction.DESC.equals(order.getDirection()) ? " desc" : "";
            strBuilder.append(StrUtil.format(",{}{}", order.getField(), orderStr));
        }
        return " order by " + strBuilder.subString(1);
    }

    private String buildSqlWhere(ComQueryInfo comQueryInfo) {
        if (EnumQueryRuleType.RULE_LIKE.equals(comQueryInfo.getQueryRuleType())) {
            comQueryInfo.setVal(buildLikeSqlVal(comQueryInfo.getVal()));
        }
        if (EnumQueryRuleType.RULE_IN.equals(comQueryInfo.getQueryRuleType())) {
            List<String> values = CollUtil.unionAll(comQueryInfo.getValues(), strToList(comQueryInfo.getVal()), ListUtil.of("empty-com"));
            return getFormat(" {} {} {} {}", comQueryInfo, ToolMysql.listToSqlStr(values));
        }
        if (comQueryInfo.getQueryRuleType().isNoVal()) {
            return getFormat(" {} {} {}", comQueryInfo, comQueryInfo.getVal());
        }
        return getFormat(" {} {} {} '{}'", comQueryInfo, comQueryInfo.getVal());
    }

    private static String getFormat(String template, ComQueryInfo comQueryInfo, String val) {
        return StrUtil.format(template, comQueryInfo.getEnumQueryLinkRule().getName(), comQueryInfo.getName(), comQueryInfo.getQueryRuleType().getContent(), val);
    }

    private static List<String> strToList(String str) {
        if (StrUtil.isNotBlank(str)) {
            if (JSONUtil.isTypeJSONArray(str)) {
                return JSONUtil.toList(str, String.class);
            }
            return StrUtil.split(str, ",");
        }
        return new ArrayList<>();
    }

    private static String buildLikeSqlVal(String val) {
        if (!StrUtil.contains(val, "%")) {
            val = StrUtil.format("%{}%", val);
        }
        return val;
    }

    public static void main(String[] args) {

    }
}
